home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Space & Astronomy
/
Space and Astronomy (October 1993).iso
/
mac
/
VIEWERS
/
AMIGA
/
GIFMACHN.LZH
/
UnDiff.c
< prev
Wrap
C/C++ Source or Header
|
1991-07-01
|
17KB
|
712 lines
; /* "execute undiff.c"
lc -j73 -cfist -L undiff.c
delete undiff.o undiff.lnk
quit
*/
/*===========================================================================
* UNDIFF.C -- Apply output from Lattice DIFF
* Copyright ⌐ 1991 by Robert L. Pyron. All Rights Reserved.
*
* You may use this program for any purpose at all. If you use any
* source code from this program, I ask that you give me credit in your
* documentation. I would be very pleased if somebody were to make
* improvements to this program and pass them on to the Amiga
* community.
*
* Lattice DIFF outputs a sequence of commands for converting one text file
* to another. Unfortunately, there is no program available which will apply
* these diffs to the original file and yield the modified file. (Or maybe
* there is, and I just didn't RTFM carefully enough!)
*
* Anyway, this is something I hacked together in about six hours --
* don't expect production-quality code here.
*
* Usage is:
*
* UNDIFF < diff_file
*
* There are very few bells and whistles here. The only one I can think of
* offhand is that you may concatenate all of your diff files into one big
* file, and feed that into UNDIFF.
*
* If the diff file says "TRANSFORM foo/bar.c TO sna/fu.c", then that is
* what you get -- no redirection of input or output files to somewhere
* else.
*
* The destination directory must already exist.
*
* If the program terminates due to an error, you will be left with a
* malformed target file.
*
* Error output is rather cryptic.
*===========================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned long ULONG;
typedef enum _ParseState
{
IDLE = 0,
PARSE_TRANSFORM = 10,PARSE_COMMAND,
DO_APPEND = 20,APPENDING_1,
DO_CHANGE = 30,CHANGING_1,CHANGING_2,CHANGING_3,CHANGING_4,CHANGING_5,
DO_DELETE = 40,DELETING_1,DELETING_2,
DO_NODIFF = 50,
ERROR = -1,IO_ERROR = -2,
DONE = 99,QUIT = 100
} ParseState;
#define BADIO -2
#define ENDFILE -1
#define EMPTY 0
#define BLANK ' '
#define TAB '\t'
#define ASTER '*'
#define NODIFF 'N'
#define DASH '-'
#define SRCLINE '<'
#define DSTLINE '>'
char srcname[256]; /* name of current source file */
char dstname[256]; /* name of current destination file */
char srctemp[256]; /* temp; src file specified in this command */
char dsttemp[256]; /* temp; dst file specified in this command */
char srcrange[32]; /* temp for decoding range specification */
char dstrange[32]; /* temp for decoding range specification */
char cmdbuf[256]; /* input buffer for dif file */
char linebuf[256]; /* buffer for moving lines from file A to file B */
ULONG src1, src2; /* source line range specified by command */
ULONG dst1, dst2; /* dest line range specified by command */
ULONG cmdline; /* current line number in dif file */
ULONG srcline; /* current line number in source file */
ULONG dstline; /* current line number in destination file */
FILE *cmdfp;
FILE *srcfp;
FILE *dstfp;
void main (int argc, char **argv);
int parse_transform (char *cmdbuf);
int parse_nodiff (char *cmdbuf);
int parse_append (char *cmdbuf);
int parse_change (char *cmdbuf);
int parse_delete (char *cmdbuf);
int parse_range (char *range, unsigned long *lo, unsigned long *hi);
int getdif (char *cmdbuf);
int getsrc (char *linebuf);
int putdst (char *linebuf);
/*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
void main (int argc, char **argv)
{
ParseState state = IDLE, laststate;
char *error = "";
int cmd, rc;
cmdfp = stdin; /****** LAZY LAZY LAZY ******/
loop:
switch (state)
{
case IDLE:
laststate = state;
/*
* We're just waiting around for something to happen.
*/
error = "unknown command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case ENDFILE: state = DONE; break;
case EMPTY: state = IDLE; break;
case BLANK: state = PARSE_TRANSFORM; break;
case TAB: state = PARSE_TRANSFORM; break;
case ASTER: state = PARSE_COMMAND; break;
case NODIFF: state = PARSE_COMMAND; break;
default: state = ERROR; break;
}
break;
case PARSE_TRANSFORM:
laststate = state;
/*
* Copy remainder of current file.
*/
if (srcfp && dstfp)
{
do
{
if ((rc = getsrc(linebuf)) == 0)
rc = putdst(linebuf);
}
while (rc == 0);
if (ferror(srcfp) || ferror(dstfp))
{ state = IO_ERROR; break; }
}
/*
* Confirm that cmdbuf contains a "TO TRANSFORM" command.
* Close current src and dst files, and open new files.
*/
error = "bad command";
if (parse_transform(cmdbuf))
{ state = ERROR; break; }
printf ("TRANSFORMING %s TO %s ...\n",
srcname, dstname);
if (srcfp) { fclose (srcfp); srcfp = NULL; }
if (dstfp) { fclose (dstfp); dstfp = NULL; }
srcfp = fopen (srcname,"r");
dstfp = fopen (dstname,"w");
srcline = dstline = 0;
state = (srcfp && dstfp) ? IDLE : IO_ERROR;
break;
case PARSE_COMMAND:
laststate = state;
/*
* cmdbuf should contain an APPEND, CHANGE, or DELETE command.
*/
error = "bad command";
if (parse_append(cmdbuf) == 0)
state = DO_APPEND;
else if (parse_change(cmdbuf) == 0)
state = DO_CHANGE;
else if (parse_delete(cmdbuf) == 0)
state = DO_DELETE;
else if (parse_nodiff(cmdbuf) == 0)
state = DO_NODIFF;
else
state = ERROR;
break;
case DO_NODIFF:
laststate = state;
error = "inconsistent file name";
if (stricmp(srcname,srctemp) || stricmp(dstname,dsttemp))
{ state = ERROR; break; }
/*
* Copy remainder of file.
*/
if (srcfp && dstfp)
{
do
{
if ((rc = getsrc(linebuf)) == 0)
rc = putdst(linebuf);
}
while (rc == 0);
if (ferror(srcfp) || ferror(dstfp))
{ state = IO_ERROR; break; }
}
state = IDLE;
break;
case DO_APPEND:
laststate = state;
/*
* cmdbuf contains an APPEND command.
* Make sure file name agrees.
*/
error = "inconsistent file name";
if (stricmp(srcname,srctemp))
{ state = ERROR; break; }
/*
* Copy unaffected lines.
*/
while (srcline < src1)
{
if (getsrc(linebuf) != 0)
{ state = IO_ERROR; break; }
if (putdst(linebuf) != 0)
{ state = IO_ERROR; break; }
}
error = "inconsistent line count";
if (srcline != src1)
{ state = ERROR; break; }
/*
* Get next dif line. Should begin with '>'.
*/
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case DSTLINE: state = APPENDING_1; break;
default: state = ERROR; break;
}
break;
case APPENDING_1:
laststate = state;
/*
* cmdbuf contains a line that should be output.
* Do so, then get next dif line.
*/
if (putdst(cmdbuf+1) != 0)
{ state = IO_ERROR; break; }
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case ENDFILE: state = DONE; break;
case EMPTY: state = IDLE; break;
case DSTLINE: state = APPENDING_1; break;
default: state = ERROR; break;
}
break;
case DO_CHANGE:
laststate = state;
/*
* cmdbuf contains a CHANGE command.
* Make sure file names agree.
*/
error = "inconsistent file name";
if (stricmp(srcname,srctemp) || stricmp(dstname,dsttemp))
{ state = ERROR; break; }
/*
* Copy unaffected lines.
*/
while (srcline < src1-1 && dstline < dst1-1)
{
if (getsrc(linebuf) != 0)
{ state = IO_ERROR; break; }
if (putdst(linebuf) != 0)
{ state = IO_ERROR; break; }
}
error = "inconsistent line count";
if (srcline != src1-1 || dstline != dst1-1)
{ state = ERROR; break; }
/*
* Get next dif line. Should begin with '<'.
*/
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case SRCLINE: state = CHANGING_1; break;
default: state = ERROR; break;
}
break;
case CHANGING_1:
laststate = state;
/*
* cmdbuf contains a line that should be "deleted".
* Make sure it matches current line in src file.
*/
if (getsrc(linebuf) != 0)
{ state = IO_ERROR; break; }
error = "line does not match";
if (strcmp(linebuf,cmdbuf+1))
{ state = ERROR; break; }
/*
* Get next dif line.
*/
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case SRCLINE: state = CHANGING_1; break;
case EMPTY: state = CHANGING_2; break;
default: state = ERROR; break;
}
break;
case CHANGING_2:
laststate = state;
/*
* We've finished the "delete" component of the change.
* Look for separator line between "delete" and "append"
* lines.
*/
error = "inconsistent line count";
if (srcline != src2)
{ state = ERROR; break; }
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case EMPTY: state = CHANGING_2; break;
case DASH: state = CHANGING_3; break;
default: state = ERROR; break;
}
break;
case CHANGING_3:
laststate = state;
/*
* cmdbuf holds separator between "delete and "append" lines.
* Next line should begin with '>'.
*/
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case DSTLINE: state = CHANGING_4; break;
default: state = ERROR; break;
}
break;
case CHANGING_4:
laststate = state;
/*
* cmdbuf contains a line that should be output.
* Do so, then get next dif line.
*/
if (putdst(cmdbuf+1) != 0)
{ state = IO_ERROR; break; }
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case DSTLINE: state = CHANGING_4; break;
case EMPTY: state = IDLE; break;
default: state = ERROR; break;
}
break;
case CHANGING_5:
laststate = state;
/*
* Check to see that we inserted the proper number of lines.
*/
error = "inconsistent line count";
state = (dstline == dst2) ? IDLE : ERROR;
break;
case DO_DELETE:
laststate = state;
/*
* cmdbuf contains an APPEND command.
* Make sure file name agrees.
*/
error = "inconsistent file name";
if (stricmp(srcname,srctemp))
{ state = ERROR; break; }
/*
* Copy unaffected lines.
*/
while (srcline < src1-1)
{
if (getsrc(linebuf) != 0)
{ state = IO_ERROR; break; }
if (putdst(linebuf) != 0)
{ state = IO_ERROR; break; }
}
error = "inconsistent line count";
if (srcline != src1-1)
{ state = ERROR; break; }
/*
* Get next dif line. Should begin with '<'.
*/
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case SRCLINE: state = DELETING_1; break;
default: state = ERROR; break;
}
break;
case DELETING_1:
laststate = state;
/*
* cmdbuf contains a line that should be "deleted".
* Make sure it matches current line in src file.
*/
if (getsrc(linebuf) != 0)
{ state = IO_ERROR; break; }
error = "line does not match";
if (strcmp(linebuf,cmdbuf+1))
{ state = ERROR; break; }
/*
* Get next dif line.
*/
error = "bad command";
switch (cmd = getdif (cmdbuf))
{
case BADIO: state = IO_ERROR; break;
case SRCLINE: state = DELETING_1; break;
case EMPTY: state = DELETING_2; break;
default: state = ERROR; break;
}
break;
case DELETING_2:
laststate = state;
/*
* Check to see that we deleted the proper number of lines.
*/
error = "inconsistent line count";
state = (srcline == src2) ? IDLE : ERROR;
break;
case ERROR:
printf ("*** ERROR: %s\n", error);
printf ("state = %d cmdline = %d srcline = %d dstline = %d\n",
(int) laststate, cmdline, srcline, dstline);
printf ("src1 = %d src2 = %d dst1 = %d dst2 = %d\n",
src1, src2, dst1, dst2);
printf ("cmdbuf = %s\n", cmdbuf);
printf ("linebuf = %s\n", linebuf);
printf ("srcrange = %s\n", srcrange);
printf ("dstrange = %s\n", dstrange);
rc = 100;
state = QUIT;
break;
case IO_ERROR:
printf ("*** I/O error\n");
rc = 200;
state = QUIT;
break;
case DONE:
/*
* Copy remainder of file.
*/
if (srcfp && dstfp)
{
do
{
if ((rc = getsrc(linebuf)) == 0)
rc = putdst(linebuf);
}
while (rc == 0);
if (ferror(srcfp) || ferror(dstfp))
{ state = IO_ERROR; break; }
}
rc = 0;
state = QUIT;
break;
case QUIT:
if (srcfp) { fclose (srcfp); srcfp = NULL; }
if (dstfp) { fclose (dstfp); dstfp = NULL; }
exit(rc);
break;
default:
error = "unknown state!";
state = ERROR;
break;
}
goto loop;
}
/*-------------------------------------------------------------------------
* Extract srcname and dstname from TRANSFORM command.
* "TO TRANSFORM <srcname> INTO <dstname> ..."
*
* Sets global variables "srcname" and "dstname".
* Returns 0 on success.
*-------------------------------------------------------------------------
*/
int parse_transform (char *cmdbuf)
{
int nc = sscanf (cmdbuf, " TO TRANSFORM %s INTO %s ...",
srcname, dstname);
return (nc != 2);
}
/*-------------------------------------------------------------------------
* Extract srcname and dstname from NODIFF command.
* "NO DIFFERENCE BETWEEN <dst> AND <src>"
*
* Sets global variables "srctemp" and "dsttemp".
* Returns 0 on success.
*-------------------------------------------------------------------------
*/
int parse_nodiff (char *cmdbuf)
{
int nc = sscanf (cmdbuf, "NO DIFFERENCE BETWEEN %s AND %s",
dsttemp, srctemp);
return (nc != 2);
}
/*-------------------------------------------------------------------------
* Extract line number and file name from APPEND command.
* "*** APPEND AFTER <n> IN <dst> ***"
*
* Returns 0 on success.
*-------------------------------------------------------------------------
*/
int parse_append (char *cmdbuf)
{
int nc = sscanf (cmdbuf, "*** APPEND AFTER %s IN %s ***",
srcrange, srctemp);
return (nc == 2) ? parse_range(srcrange,&src1,&src2) : -1;
}
/*-------------------------------------------------------------------------
* Extract info from CHANGE command.
* "*** CHANGE <range> IN <src> TO <range> IN <dst> ***",
*
* Returns 0 on success.
*-------------------------------------------------------------------------
*/
int parse_change (char *cmdbuf)
{
int nc = sscanf (cmdbuf, "*** CHANGE %s IN %s TO %s IN %s ***",
srcrange, srctemp, dstrange, dsttemp);
if (nc != 4)
return -1;
else if (parse_range(srcrange,&src1,&src2) != 0)
return -1;
else if (parse_range(dstrange,&dst1,&dst2) != 0)
return -1;
return 0;
}
/*-------------------------------------------------------------------------
* Extract line number and file name from DELETE command.
* "*** DELETE <range> FROM <src> ***",
*
* Returns 0 on success.
*-------------------------------------------------------------------------
*/
int parse_delete (char *cmdbuf)
{
int nc = sscanf (cmdbuf, "*** DELETE %s FROM %s ***",
srcrange, srctemp);
return (nc == 2) ? parse_range(srcrange,&src1,&src2) : -1;
}
/*-------------------------------------------------------------------------
* Parse a range specifier in the form "n" or "[n,n]".
* Returns 0 on success.
*-------------------------------------------------------------------------
*/
int parse_range (char *range, ULONG *lo, ULONG *hi)
{
*lo = *hi = -1;
if (sscanf (range,"[%lu,%lu]", lo, hi) == 2)
return 0;
else if (sscanf (range,"%lu", lo) == 1)
{
*hi = *lo;
return 0;
}
else
{
printf ("****** cannot scan range %s ******\n", range);
return -1;
}
}
/*-------------------------------------------------------------------------
* Read line from dif file. Strip trailing newline.
* Return first character (or negative value for error or eof).
*-------------------------------------------------------------------------
*/
int getdif (char *cmdbuf)
{
int len;
if (fgets(cmdbuf,255,cmdfp))
{
cmdbuf[255] = '\0';
len = strlen(cmdbuf);
if (len && cmdbuf[--len] == '\n')
cmdbuf[len] = '\0';
cmdline++;
return (int) cmdbuf[0];
}
else if (feof(cmdfp))
return ENDFILE;
else
return BADIO;
}
/*-------------------------------------------------------------------------
* Read line from source file; increment counter.
*-------------------------------------------------------------------------
*/
int getsrc (char *linebuf)
{
char *p;
int len;
p = fgets(linebuf,255,srcfp);
if (p && (len = strlen(p)) && (p[--len] == '\n'))
p[len] = '\0';
linebuf[255] = '\0';
srcline++;
return (p == NULL);
}
/*-------------------------------------------------------------------------
* Write line to destination file; increment counter.
*-------------------------------------------------------------------------
*/
int putdst (char *linebuf)
{
int rc = fputs (linebuf,dstfp);
fputc ('\n',dstfp);
dstline++;
return rc;
}